package org.ch02.hw.demo09;

import org.ch02.hw.demo07.SqlGenerate;
import org.ch02.hw.demo10.Staffs;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.sql.*;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * @author ZJL
 * @date 2025-03-19
 */
public class DbUtil {

    /**
     * 定义 Set 集合 - 收集所有的类型转换器
     */
    private static final Set<String> CONVERT_HANDLERS = new HashSet<String>();

    /**
     * 注册类型转换器 - todo:Java SPI -解耦
     */
    static {
        CONVERT_HANDLERS.add("org.ch02.hw.demo09.converter.DateConverter");
        CONVERT_HANDLERS.add("org.ch02.hw.demo09.converter.LocalDateConverter");
        CONVERT_HANDLERS.add("org.ch02.hw.demo09.converter.LocalDateTimeConverter");
        CONVERT_HANDLERS.add("org.ch02.hw.demo09.converter.LocalTimeConverter");
    }

    //学过泛型的可以把 Object 改为泛型
    public static Object select4Bean(Class<?> beanClass, Map<String, Object> cond) {
        // 定义 SQL 语句
        String sql = SqlGenerate.generateSelect(beanClass, cond);

        // 获取连接对象
        Connection connection = DruidUtil.getConnection();
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        // 预编译 SQL 语句
        try {
            preparedStatement = connection.prepareStatement(sql);

            // 填充参数
            setParameter(cond, preparedStatement);

            // 执行查询操作 - resultSet 中，存储当前查询的数据 - select 1,2,3,4 from table
            resultSet = preparedStatement.executeQuery();

            // 把结果集中的数据映射到实体对象中，并返回 - 基于反射（内省）的技术，实现对象的映射
            // 获取源数据对象 - 获取结果集的相关信息
            ResultSetMetaData metaData = resultSet.getMetaData();

            // 把结果集中的数据映射到实体对象中，并返回 - 基于反射（内省）技术，实现对象的映射
            if (resultSet.next()) {
                // 返回初始化的Bean对象
                return getBean(beanClass, resultSet);
            }
        } catch (Exception e) {
            throw new RuntimeException("查询失败", e);
        } finally {
            DruidUtil.close(connection, preparedStatement, resultSet);
        }
        return null;
    }

    /**
     * 给占位符传入数据
     *
     * @param cond              Map集合的长度 - 占位符的数量
     * @param preparedStatement - 预编译 SQL
     * @throws SQLException
     */
    private static void setParameter(Map<String, Object> cond, PreparedStatement preparedStatement) throws SQLException {
        int index = 1;
        for (Map.Entry<String, Object> entry : cond.entrySet()) {
            preparedStatement.setObject(index++, entry.getValue());
        }
    }


    public static Object select5Bean(Class<?> beanClass, Map<String, Object> cond) {
        // 定义 SQL 语句
        String sql = SqlGenerate.generateSelect(beanClass, cond);

        // 获取连接对象
        Connection connection = DruidUtil.getConnection();
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        // 预编译 SQL 语句
        try {
            preparedStatement = connection.prepareStatement(sql);

            // 填充参数
            setParameter(cond, preparedStatement);

            // 执行查询操作 - resultSet 中，存储当前查询的数据
            resultSet = preparedStatement.executeQuery();

            if (resultSet.next()) {
                Object bean = beanClass.getConstructor().newInstance();
                Class<?> aClass = bean.getClass();
                Field[] fields = aClass.getDeclaredFields();
                for (Field field : fields) {
                    field.setAccessible(true);
                    field.set(bean, resultSet.getObject(SqlGenerate.convertToSnake(field.getName())));
                }
                return bean;
            }
        } catch (Exception e) {
            throw new RuntimeException("查询失败", e);
        } finally {
            DruidUtil.close(connection, preparedStatement, resultSet);
        }
        return null;
    }

    private static Object getBean(Class<?> beanClass, ResultSet resultSet) throws SQLException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException, NoSuchFieldException, ClassNotFoundException {
        // 1.获取元数据对象 - 获取结果集的相关信息
        ResultSetMetaData metaData = resultSet.getMetaData();

        // 2.基于元数据对象获取总列数
        int columnCount = metaData.getColumnCount();

        // 3.实例化 Bean 对象
        Object bean = beanClass.getConstructor().newInstance();

        // 4.循环遍历各列
        for (int i = 1; i <= columnCount; i++) {
            // 5.基于元数据对象，获取查询表的列名称 - sta_pos -> staPos
            String columnLabel = metaData.getColumnLabel(i);

            // 6.基于数据表字段名称，通过反射获取实体对象对应 Field 对象
            Field beanField = beanClass.getDeclaredField(SqlGenerate.convertToCamelCase(columnLabel));

            // 7.根据字段名称，获取各列数据 -
            // 结果集获取数据的数据类型与实体对象属性的数据类型不一致，导致异常！
            Object data = resultSet.getObject(columnLabel);

            if (data != null) {
                // 遍历所有的类型转换器
                for (String convertHandler : CONVERT_HANDLERS) {
                    // 基于反射，实例化转换器对象
                    TypeConverter convert = (TypeConverter) Class
                            .forName(convertHandler)
                            .getConstructor().newInstance();

                    // 判断是否需要转换
                    if (convert.supports(beanField.getType())) {
                        data = convert.apply(beanField.getType(), data);
                    }
                }
            }

            // 判断 Bean 对象的当前字段是否为基本类型，并且获取数据表的数据为 null
            // 基本类型不能赋值为 null
            if (beanField.getType().isPrimitive() && data == null) {
                continue;
            }

            // 8.初始化 Bean 对象 - 把查询的字段数据映射到Bean对象相应的属性中
            beanField.setAccessible(true);
            beanField.set(bean, data);
        }
        return bean;
    }

    /**
     * 添加数据
     *
     * @param staffs Bean 对象
     * @return 受影响的行数
     */
    public static int insert(org.ch02.hw.demo10.Staffs staffs ) {
        int row = 0;
        // 定义要操作的 SQL 语句
        String sql = SqlGenerate.generateInsert(staffs.getClass());
        // 获取连接对象
        Connection connection = DruidUtil.getConnection();
        PreparedStatement preparedStatement = null;
        try {
            // 预编译 SQL 语句
            preparedStatement = connection.prepareStatement(sql);

            // 给占位符填充数据
            // 获取 Bean 对象的 Class 对象
            Class<? extends Staffs> aClass = staffs.getClass();
            // 获取 Bean 对象所有的字段
            Field[] fields = aClass.getDeclaredFields();
            // 循环遍历所有字段，给占位符填充数据
            for (int i = 0; i < fields.length; i++) {
                // 开启权限
                fields[i].setAccessible(true);
                // 获取当前遍历字段的数据
                Object currentFieldData = fields[i].get(staffs);
                preparedStatement.setObject(i + 1, currentFieldData);
            }
            row = preparedStatement.executeUpdate();
        } catch (Exception e) {
            throw new RuntimeException("添加数据失败！");
        } finally {
            // 释放对象
            DruidUtil.close(connection, preparedStatement, null);
        }
        return row;
    }
}

